package com.sikaryofficial.backend.service.brand.export;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sikaryofficial.backend.domain.vo.export.ProductExportVO;
import com.sikaryofficial.backend.domain.vo.export.ProductImportVO;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.system.api.model.att.AttachmentResp;
import com.sikaryofficial.system.api.model.product.BrandTasteDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.sikaryofficial.backend.utils.ProductUtil.buildProductMd5;

@Slf4j
@Getter
@Setter
public class ProductImportListener extends AnalysisEventListener<ProductImportVO> {

    private List<BrandTasteDTO> dataList = Lists.newArrayList();
    private Long userId;

    public ProductImportListener(Long userId) {
        this.userId = userId;
    }

    private static void checkThird(List<String> headNames) {
        StringBuilder sb = new StringBuilder();
        Map<String, Integer> map = Maps.newHashMap();
        for (String item : headNames) {
            if (map.containsKey(item)) {
                map.put(item, MapUtil.getInt(map, item, 0) + 1);
            } else {
                map.put(item, 1);
            }
        }
        map.forEach((key, value) -> {
            if (Objects.nonNull(value) && value > 1) {
                sb.append("[").append(key).append("]、");
            }
        });
        if (StringUtils.isNotBlank(sb.toString())) {
            throw new ServiceException(
                    "Excel中有多余表头信息：" + sb);
        }
    }

    @Override
    public void invoke(ProductImportVO productImportVO, AnalysisContext analysisContext) {
        if (Objects.isNull(productImportVO)) {
            return;
        }
        Integer rowNum = analysisContext.readRowHolder().getRowIndex();
        String errMsg;
        try {
            //根据excel数据实体中的javax.validation + 正则表达式来校验excel数据(格式校验：字符长度，类型，格式，侏儒时间，手机号，身份证都可进行校验)
            errMsg = EasyExcelValidHelper.validateEntity(productImportVO);
            if (StringUtils.isNotBlank(errMsg)) {
                throw new ServiceException("Excel内容有错误，在第" + rowNum + "行：" + errMsg);
            }
        } catch (NoSuchFieldException e) {
            log.info("解析数据出错" + e.getMessage());
        }
        BrandTasteDTO brandTasteDTO = new BrandTasteDTO();

        brandTasteDTO.setProductName(productImportVO.getProductNameEn());
        brandTasteDTO.setProductNameCn(productImportVO.getProductNameCn());
        brandTasteDTO.setModel(productImportVO.getModelEn());
        brandTasteDTO.setModelCn(productImportVO.getModelCn());
        brandTasteDTO.setBrandNo(productImportVO.getBrandNoEn());
        brandTasteDTO.setBrandNoCn(productImportVO.getBrandNoCn());
        brandTasteDTO.setStyle(productImportVO.getStyleEn());
        brandTasteDTO.setStyleCn(productImportVO.getStyleCn());
        brandTasteDTO.setTasteName(productImportVO.getTasteNameEn());
        brandTasteDTO.setTasteNameCn(productImportVO.getTasteNameCn());
        brandTasteDTO.setQrUrl("");
        brandTasteDTO.setQrTitle(productImportVO.getQrTitleEn());
        brandTasteDTO.setVisible(productImportVO.getVisible());
        brandTasteDTO.setQrMatlNo(productImportVO.getQrMatlNo());
        brandTasteDTO.setRemark(productImportVO.getProductIntroduceEn());
        brandTasteDTO.setRemarkCn(productImportVO.getProductIntroduceCn());

        List<AttachmentResp> attachmentList = Lists.newArrayList();
        brandTasteDTO.setAttachmentList(attachmentList);
        // 附件数据
        buildAttachment(productImportVO, attachmentList);
        // md5字段设置-业务上建立唯一性
        brandTasteDTO.setMd5(buildProductMd5(
                brandTasteDTO.getBrandNo(), brandTasteDTO.getModel(),
                brandTasteDTO.getStyle(), brandTasteDTO.getTasteName()));
        dataList.add(brandTasteDTO);
    }

    /**
     * 附件数据
     *
     * @param productImportVO
     * @param attachmentList
     */
    private void buildAttachment(ProductImportVO productImportVO, List<AttachmentResp> attachmentList) {
        // hutool 通过反射工具，获取ProductImportVO 类的所有get方法
        Method[] methods = ReflectUtil.getMethods(ProductImportVO.class, method -> method.getName().startsWith("getUrl"));
        // 遍历 methods
        for (Method method : methods) {
            Object obj = ReflectUtil.invoke(productImportVO, method);
            if (Objects.isNull(obj)) {
                continue;
            }
            attachmentList.add(AttachmentResp.builder().url(obj.toString()).build());
        }
    }

    /**
     * 表头信息校验
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //输入文件的表头-名称列表
        if (CollUtil.isEmpty(headMap.values())) {
            throw new ServiceException("Excel表头信息为空.");
        }
        List<String> headNames = new ArrayList<>(headMap.values());
        //系统模板对应的表头-名称列表
        // DeptCustomConfigVo
        List<String> annotationValues = getFieldAnnotationValue();
        if (CollUtil.isEmpty(annotationValues)) {
            throw new ServiceException("程序中没有添加注解ExcelProperty.");
        }
        checkThird(headNames);
        checkFirst(headNames, annotationValues);
        checkSecond(headNames, annotationValues);
    }

    /**
     * 1、导入的表格的表头字段缺少：导入失败，toast提示：Ecexl缺少以下表头信息：[XXX]、[XXX]、[XXX]。
     * <p>
     * 2、导入的表格的表头字段有误：导入失败，toast提示：Excel中以下表头不正确：[XXX]、[XXX]、[XXX]。
     * <p>
     * 3、导入的表格的表头字段有多余：可以正常导入（只取“部门ID、部门名称、门店编码、市场活码编码“四个字段中的数据，如果有重复，只前前一个字段所在列的数据）
     * <p>
     * 4、导入的表格的表头顺序错乱：可以正常导入
     * <p>
     * 5、导入的表格的部门ID数据中有不存在的部门ID：可以正常导入，toast提示：移除不存在的deptld=XXXX，成功导入【XXX】行。
     * <p>
     * 6、导入的表格的部门ID数据中有存在空格：可以正常导入，跳过该空格所在行的数据，导入其他正常数据
     * <p>
     * 7、导入的表格中除部门ID外其他数据存在空格：可以正常导入，覆盖数据（门店编码、市场活动编码，部门名称不覆盖）
     * <p>
     * 8、导入的表格中部门ID数据存在重复的情况：导入失败，toast提示：部门ID异常，[xxx]重复、[xxx]重复、[xxx]重复，请修改后重新上传。
     * <p>
     * 9、导入的表格中只有表头字段，无具体数据（或者表头字段也没有）：导入失败，toast提示：文件内容为空
     */
    private void checkFirst(List<String> headNames, List<String> annotationValues) {
        StringBuilder sb = new StringBuilder();
        for (String item : annotationValues) {
            if (!headNames.contains(item)) {
                sb.append("[").append(item).append("]、");
            }
        }
        if (StringUtils.isNotBlank(sb.toString())) {
            throw new ServiceException("Excel缺少以下表头信息：" + sb);
        }
    }

    private void checkSecond(List<String> headNames, List<String> annotationValues) {
        StringBuilder sb = new StringBuilder();
        for (String item : headNames) {
            if (!annotationValues.contains(item)) {
                sb.append("[").append(item).append("]、");
            }
        }
        if (StringUtils.isNotBlank(sb.toString())) {
            throw new ServiceException(
                    "Excel中以下表头不正确：" + sb);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //do nothing
    }

    /**
     * 获取字段特定注解对应的属性值
     *
     * @return
     */
    private List<String> getFieldAnnotationValue() {
        Field[] fields = ReflectUtil.getFields(ProductExportVO.class);
        List<String> headerNames = Lists.newArrayList();
        for (Field item : fields) {
            ExcelProperty excelProperty = item.getAnnotation(ExcelProperty.class);
            if (Objects.isNull(excelProperty)) {
                continue;
            }
            String[] heads = excelProperty.value();
            Collections.addAll(headerNames, heads);
        }
        return headerNames;
    }
}
